/***************************************************************
*  Copyright (c) 2007 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.search.strategy;

import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.sql.DataSource;

import net.groupme.model.Group;
import net.groupme.model.MODEL_CONSTANTS;
import net.groupme.model.ObjectFactory;
import net.groupme.model.Resource;
import net.groupme.model.Tag;
import net.groupme.model.dao.DATABASE_CONSTANTS;
import net.groupme.search.strategy.model.WeightedIdentifiableComparatorByWeight;
import net.groupme.search.strategy.model.WeightedResource;

import org.springframework.jdbc.object.MappingSqlQuery;

/**
 * created on Oct 9, 2007
 * 
 * This class/interface...
 * 
 * @author Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by: $Author: fabian $
 * 
 * @version $Revision: 1.1 $ $Date: 2007-10-09 13:02:37 $
 */
public class SearchBasedOnGroupAndTimeContext {

	

	/** weight of resources that are contained in directly tagged groups */
	private static final double GROUP_CONTEXT_WEIGHT = 1.0;
	/** weight of directly tagged resources/groups */
	private static final double DIRECT_WEIGHT = 2.0;
	/** weight of groups that contain directly tagged resources */
	private static final double RESOURCES_IN_GROUP_WEIGHT = 1.0;


	/* (non-Javadoc)
	 * @see net.groupme.search.strategy.SearchStrategy#searchForResources(java.util.Collection, boolean, boolean)
	 */
	public List<Resource> searchForResources(Collection<Tag> tags, boolean allTags, boolean noGroups, Date date) {
		Collection<List<WeightedResource>> scoringAccordingToTag = new ArrayList<List<WeightedResource>>();
		for(Tag tag: tags){
			scoringAccordingToTag.add(searchForResources(tag, noGroups, date));
		}
		if(scoringAccordingToTag == null || scoringAccordingToTag.isEmpty()){
			return new ArrayList<Resource>();
		}
		int numberOfTags = 0;
		//weight results (just make a sum):
		List<WeightedResource> overallScoring = new ArrayList<WeightedResource>();
		for(List<WeightedResource> weightedResources: scoringAccordingToTag){
			if(weightedResources != null){
			for(WeightedResource weightedResource: weightedResources){
				if(numberOfTags == 0){
					overallScoring.add(weightedResource);
				}else if(overallScoring.contains(weightedResource)){ //Note, that WeightedResource are compared, by their Resource and not by the weight-value!!!
					int pos = overallScoring.indexOf(weightedResource);
					overallScoring.get(pos).weight += weightedResource.weight;
				}else if(!allTags){
					overallScoring.add(weightedResource);
				}
			}
			
			}
			numberOfTags++;
		}
		Collections.sort(overallScoring, new WeightedIdentifiableComparatorByWeight());
		
		//put resources of WeightedResources into a list:
		List<Resource> resultList = new ArrayList<Resource>();
		for(int i=0; i<overallScoring.size(); i++){
			resultList.add(overallScoring.get(i).resource);
		}
		return resultList;
	}
	
	@SuppressWarnings("unchecked")
	protected List<WeightedResource> searchForResources(Tag tag, boolean noGroups, Date date){
		//Number of Users who tagged Resource X directly with tag:
		String query = 
			"SELECT " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + ", count(" + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + ") " + 
				" FROM " + DATABASE_CONSTANTS.TABLE_USERTAGGROUPRESOURCE + 
				" WHERE " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_DATE + " < '" + date + "' AND " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_TAGID + " = '" + tag.getId() + "'" +
				(noGroups ? (" AND " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + " in " +
						"(SELECT " + DATABASE_CONSTANTS.RESOURCE_ID + " FROM " + DATABASE_CONSTANTS.TABLE_RESOURCE  + 
						" WHERE " + DATABASE_CONSTANTS.RESOURCE_TYPE + " != '" +  MODEL_CONSTANTS.RESOURCE_TYPE_GROUP + "')") : "") + 
				" group by " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + 
				" order by count(" + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + ") DESC";
		GetWeightedResource scoringQuery = new GetWeightedResource(ObjectFactory.ds, query);
		Iterator iter = scoringQuery.execute().iterator();
		List<WeightedResource> resources = null;
		if (iter.hasNext()){
			resources = (List<WeightedResource>) iter.next();
		}
		
		//resources contained in directly tagged groups:
		query = 
			"SELECT " + DATABASE_CONSTANTS.GROUPRESOURCE_RESOURCE_ID + ", count(" + DATABASE_CONSTANTS.GROUPRESOURCE_RESOURCE_ID + ") " + 
				" FROM " + DATABASE_CONSTANTS.TABLE_GROUPRESOURCE +
				" WHERE " + DATABASE_CONSTANTS.GROUPRESOURCE_GROUP_ID + " in " +
					" ( SELECT distinct " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + " FROM " + DATABASE_CONSTANTS.TABLE_USERTAGGROUPRESOURCE + 
				" WHERE " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_DATE + " < '" + date + "' AND " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_TAGID + " = '" + tag.getId() + "' AND " +
					DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + " in " +
							"(SELECT " + DATABASE_CONSTANTS.RESOURCE_ID + " FROM " + DATABASE_CONSTANTS.TABLE_RESOURCE  + 
								" WHERE " + DATABASE_CONSTANTS.RESOURCE_TYPE + " = '" +  MODEL_CONSTANTS.RESOURCE_TYPE_GROUP + "')" + 
								" group by " + DATABASE_CONSTANTS.USERTAGGROUPRESOURCE_RESOURCEID + ") " +
				" group by " + DATABASE_CONSTANTS.GROUPRESOURCE_RESOURCE_ID + 
				" order by count(" + DATABASE_CONSTANTS.GROUPRESOURCE_RESOURCE_ID + ") DESC";
		
		scoringQuery = new GetWeightedResource(ObjectFactory.ds, query);
		iter = scoringQuery.execute().iterator();
		List<WeightedResource> groupResources = null;
		if (iter.hasNext()){
			groupResources = (List<WeightedResource>) iter.next();
		}
		
		if(resources != null){
			if(groupResources != null){
				for(WeightedResource res: groupResources){
					if(resources.contains(res)){
						resources.get(resources.indexOf(res)).weight +=  ((GROUP_CONTEXT_WEIGHT / DIRECT_WEIGHT) * res.weight);
					}else{
						res.weight = ((GROUP_CONTEXT_WEIGHT / DIRECT_WEIGHT) * res.weight);
						resources.add(res);
					}
				}
			}
			Collections.sort(resources, new WeightedIdentifiableComparatorByWeight());
			return resources;
		}else{ //will never happen :)
			return groupResources;
		}
	}
	


	/**
	 * Small helper class to query scoring of resources.
	 */
	public class GetWeightedResource extends MappingSqlQuery{
		/**
		 * Creates a new GetWeightedResource instance.
		 * @param ds the data source
		 * @param sql SQL query which has only a resourceId and a score column
		 */
		public GetWeightedResource(DataSource ds, String sql) {
			super(ds, sql);
		}

		/* (non-Javadoc)
		 * @see org.springframework.jdbc.object.MappingSqlQuery#mapRow(java.sql.ResultSet, int)
		 */
		@Override
		protected Object mapRow(ResultSet rs, int rowNum) throws SQLException {
			List<WeightedResource> resultRanking = new ArrayList<WeightedResource>();
			while (!rs.isAfterLast()){
				resultRanking.add(new WeightedResource(ObjectFactory.getResource(rs.getString(1)), rs.getDouble(2)));
				rs.next();
			}
			return resultRanking;
		}
		
	}
}
